/***************************************************************************
 *   Copyright (C) 2008 by Pino Toscano <pino@kde.org>                     *
 *   Copyright (C) 2008 by Harri Porten <porten@kde.org>                   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 ***************************************************************************/

#include "kjs_util_p.h"

#include <kjs/kjsobject.h>
#include <kjs/kjsprototype.h>
#include <kjs/kjsarguments.h>

#include <QUrl>
#include <QLocale>
#include <QDebug>
#include <QRegularExpression>
#include <QDateTime>

#include <cmath>

using namespace Okular;

static KJSPrototype *g_utilProto;

static KJSObject crackURL( KJSContext *context, void *,
                           const KJSArguments &arguments )
{
    if ( arguments.count() < 1 )
    {
        return context->throwException( QStringLiteral("Missing URL argument") );
    }
    QString cURL = arguments.at( 0 ).toString( context );
    QUrl url(QUrl::fromLocalFile(cURL) );
    if ( !url.isValid() )
    {
        return context->throwException( QStringLiteral("Invalid URL") );
    }
    if ( url.scheme() != QLatin1String( "file" )
         || url.scheme() != QLatin1String( "http" )
         || url.scheme() != QLatin1String( "https" ) )
    {
        return context->throwException( QStringLiteral("Protocol not valid: '") + url.scheme() + QLatin1Char('\'') );
    }

    KJSObject obj;
    obj.setProperty( context, QStringLiteral("cScheme"), url.scheme() );
    if ( !url.userName().isEmpty() )
        obj.setProperty( context, QStringLiteral("cUser"), url.userName() );
    if ( !url.password().isEmpty() )
        obj.setProperty( context, QStringLiteral("cPassword"), url.password() );
    obj.setProperty( context, QStringLiteral("cHost"), url.host() );
    obj.setProperty( context, QStringLiteral("nPort"), url.port( 80 ) );
    // TODO cPath       (Optional) The path portion of the URL.
    // TODO cParameters (Optional) The parameter string portion of the URL.
    if ( url.hasFragment() )
        obj.setProperty( context, QStringLiteral("cFragments"), url.fragment(QUrl::FullyDecoded) );

    return obj;
}

static KJSObject printd( KJSContext *context, void *,
                           const KJSArguments &arguments )
{
    if ( arguments.count() < 2 )
    {
        return context->throwException( QStringLiteral("Invalid arguments") );
    }

    KJSObject oFormat = arguments.at( 0 );
    QString format;
    QLocale defaultLocale;

    if( oFormat.isNumber() )
    {
        int formatType = oFormat.toInt32( context );
        switch( formatType )
        {
            case 0:
                format = QStringLiteral( "D:yyyyMMddHHmmss" );
                break;
            case 1:
                format = QStringLiteral( "yyyy.MM.dd HH:mm:ss");
                break;
            case 2:
                format = defaultLocale.dateTimeFormat( QLocale::ShortFormat );
                if( !format.contains( QStringLiteral( "ss" ) ) )
                    format.insert( format.indexOf( QStringLiteral( "mm" ) ) + 2,  QStringLiteral( ":ss" ) );
                break;
        }
    }
    else
    {
        format = arguments.at( 0 ).toString( context ).replace( QLatin1String("tt"), QLatin1String("ap") );
        format.replace( 't', 'a' );
        for( QChar &formatChar : format )
        {  
            if( formatChar == 'M' )
                formatChar = 'm';
            else if( formatChar == 'm' )
                formatChar = 'M';
        }
    }

    QLocale locale( QStringLiteral("en_US") );
    const QStringList str = arguments.at( 1 ).toString( context ).split( QRegularExpression( QStringLiteral("\\W") ) );
    QString myStr = QStringLiteral( "%1/%2/%3 %4:%5:%6" ).arg( str[1], str[2], str[3], str[4], str[5], str[6] );
    QDateTime date = locale.toDateTime( myStr, QStringLiteral( "MMM/d/yyyy H:m:s" ) );


    return KJSString( defaultLocale.toString( date, format ) );
}

/** Converts a Number to a String using l10n
 *
 * String numberToString( Number number, String format = 'g', int precision = 6,
 *                        String LocaleName = system )
 */
static KJSObject numberToString ( KJSContext *context, void *,
                                  const KJSArguments &arguments )
{
    if ( arguments.count() < 1 )
    {
        return context->throwException( QStringLiteral( "Invalid arguments" ) );
    }

    const double number = arguments.at( 0 ).toNumber( context );
    if ( std::isnan( number ) )
    {
        return KJSString( "NaN" );
    }

    QChar format = QLatin1Char( 'g' );
    if ( arguments.count() >= 2 )
    {
        const QString fmt = arguments.at( 1 ).toString( context );
        if ( !fmt.isEmpty() )
        {
            format = fmt[0];
        }
    }

    int precision = 6;
    if ( arguments.count() >= 3 )
    {
        precision = arguments.at( 2 ).toInt32( context );
    }

    QLocale locale;
    if ( arguments.count() == 4 )
    {
        locale = QLocale( arguments.at( 3 ).toString( context ) );
    }

    return KJSString( locale.toString( number, format.toLatin1(), precision ) );
}

/** Converts a String to a Number trying with the current locale first and
 * if that fails trying with the reverse locale for the decimal separator
 *
 * Number stringToNumber( String number ) */
static KJSObject stringToNumber ( KJSContext *context, void *,
                                  const KJSArguments &arguments )
{
    if ( arguments.count() < 1 )
    {
        return context->throwException( QStringLiteral( "Invalid arguments" ) );
    }

    const QString number = arguments.at( 0 ).toString( context );
    if ( number.isEmpty() )
    {
        return KJSNumber( 0 );
    }

    const QLocale locale;
    bool ok;
    double converted = locale.toDouble( number, &ok );

    if ( !ok )
    {
        const QLocale locale2( locale.decimalPoint() == QLatin1Char('.') ? QStringLiteral("de") : QStringLiteral("en") );
        converted = locale2.toDouble( number, &ok );
        if ( !ok )
        {
            return KJSNumber( std::nan( "" ) );
        }
    }

    return KJSNumber( converted );
}

void JSUtil::initType( KJSContext *ctx )
{
    static bool initialized = false;
    if ( initialized )
        return;
    initialized = true;

    g_utilProto = new KJSPrototype();
    g_utilProto->defineFunction( ctx, QStringLiteral("crackURL"), crackURL );
    g_utilProto->defineFunction( ctx, QStringLiteral("printd"), printd );
    g_utilProto->defineFunction( ctx, QStringLiteral("stringToNumber"), stringToNumber );
    g_utilProto->defineFunction( ctx, QStringLiteral("numberToString"), numberToString );
}

KJSObject JSUtil::object( KJSContext *ctx )
{
    return g_utilProto->constructObject( ctx, nullptr );
}

